%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Interdisciplinary Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% imagesToVideo.m
% getFileSequenceFromRepresetative.m
% readImageByIndex.m
% readImageFile.m
% readdpx.m
% createMissingDirs.m
% partitionFileName.m
% Image processing toolbox
% ffmpeg command line utilities
%
%==========================================================================

%==========================================================================
% The function "imagesAndAudioToMovie" takes an audio file and a sequence
% of images. It reduces the images to a number of specified sizes. For each
% size the reduced images are written to a single silent-video file at a
% specified frame rate, and each of those silent-video files is paired with
% the given audio file to produce a movie (i.e., video with audio).
% INPUT: "audioFileName" is a string specifying the name of the audio file
% to be used in creating the talking pictures.
% "imageFileName" is a string specifying the name of an image file which is
% part of a whole sequence of image files of the same file format residing
% in the same directory.
% "firstFileInd" and "lastFileInd" are non-negative integers specifying the
% a range of indices outside of which images are not to be part of the
% video.
% "movieDirPath" is a string specifying a directory (including full
% directory path) where movie files are to be written.
% "movieFileSuffix" is a string specifying the common suffix in the names
% of the movie files. It is the string after the indexing portion of the
% names including the dot and the file extension.
% "videoDirPath" is a string specifying a directory (including full
% directory path) where intermediate silent-video files are to be written.
% "videoFileSuffix" is a string specifying the common suffix in the names
% of the silent-video files. It is the string after the indexing portion of
% the names including the dot and the file extension.
% "audioDelay" is a real number specifying the delay of the audio with
% respect to the video measured in number of frames. A negative value
% advances the audio.
% "frameRate" is an optional positive integer specifying the desired frame
% rate of the video, i.e., the number of images to be packed in one second 
% of video.
% "desiredSizesCellArr" is an optional cell vector of positive integers or
% strings. Each entry specifies a desired length for the shorter side of
% the images to be used for making video. Positive integers are interpreted
% as length in pixels; the string 'full' specifies that the images in their
% original size are to be used.
% "videoQuality" is an optional integer from 0 through 100 specifying the
% quality of the compressed video. The higher the number, the better the
% video quality.
% "doOverwriteExisting" is an optional boolean variable specifying whether
% or not any existing output files are to be overwritten. By default they
% are overwritten without prompting.
% "waitbarStruct" is an optional parameter specifying a structure of
% parameters used to set a waitbar. Those parameters are waitbar handle,
% starting position on the waitbar (number between 0 and 1), relative
% length along the waitbar (number between 0 and (1 - starting position)),
% initial value for a counter that counts the events whose progress is
% being illustrated by the waitbar, and total reflecting the total number
% of events of the said kind.
% OUTPUT: "movieFileNameCellArr" returns a cell array of strings each being
% the name of a produced video file. The number of cells equals the number
% of specified reduction factors.
%==========================================================================
function movieFileNameCellArr = imagesAndAudioToMovie(audioFileName, ...
    imageFileName, firstFileInd, lastFileInd, movieDirPath, movieFileSuffix, ...
    videoDirPath, videoFileSuffix, audioDelay, frameRate, desiredSizesCellArr, ...
    videoQuality, doOverwriteExisting, waitbarStruct)


if (nargin < 4)
    lastFileInd = inf;
end
if (nargin < 5)
    movieDirPath = '';
end
if (nargin < 6)
    movieFileSuffix = '.mov';
end
if (nargin < 7)
    videoDirPath = movieDirPath;
end
if (nargin < 8)
    videoFileSuffix = '.mp4';
end
if (nargin < 9)
    audioDelay = 0;
end
if (nargin < 10)
    frameRate = 16;
end
if (nargin < 11)
    desiredSizesCellArr = {300; 1080};
end
if (nargin < 12)
    videoQuality = 75;
end
if (nargin < 13)
    doOverwriteExisting = true;
end


% Assert that the input audio file exists.
assert((exist(audioFileName, 'file') == 2), ...
    'SoundRestoration:imagesAndAudioToMovie:InAudioFileInexisting', ...
    ['The audio file %s can not be found.\n' ...
    'A non-empty string specifying a full directory path to an existing file is needed.'], audioFileName);
% Assert that the input image file exists.
assert((exist(imageFileName, 'file') == 2), ...
    'SoundRestoration:imagesAndAudioToMovie:InImageFileInexisting', ...
    ['The image file %s can not be found.\n' ...
    'A non-empty string specifying a full directory path to an existing file is needed.'], imageFileName);

% Find the common prefix in the names of the image files.
[~, imageFileNamePrefixNoPath] = getFileSequenceFromRepresetative(imageFileName);

% Produce video files out of the given images reducing them as specified.
videoOnlyStr = '_VideoOnly';
videoOnlyFileSuffix = [videoOnlyStr videoFileSuffix];
scratchFileNamePrefix = [videoDirPath imageFileNamePrefixNoPath];
videoFileNameCellArr = imagesToVideo(imageFileName, firstFileInd, lastFileInd, ...
    scratchFileNamePrefix, videoOnlyFileSuffix, audioDelay, frameRate, ...
    desiredSizesCellArr, videoQuality, doOverwriteExisting, waitbarStruct(1));

% Read in the audio.
[soundArr, samplingRate, bitsPerSample] = wavread(audioFileName);
% Make a smooth transition from and to silence at the beginning and at
% the end of the audio signal.
transitionTime = 0.5;  % transition time in seconds
numSamples = samplingRate*transitionTime;
% Make sure that the transition samples are not more than the available
% samples in the audio signal. This is relevant only for very short image
% sequences.
numSamples = min(numSamples, floor(numel(soundArr)/2));
transitionMask = tukeywin(2*numSamples + 1, 1);
transitionMask = transitionMask(1:numSamples);
soundArr(1:numSamples) = transitionMask.*soundArr(1:numSamples);
soundArr((end - numSamples + 1):end) = transitionMask(end:-1:1).*soundArr((end - numSamples + 1):end);

% Compute the number of samples to be added at the beginning of the
% audio signal.
numPaddingZeros = round(samplingRate*(abs(audioDelay)/frameRate));

% Pad the audio signal with silence.
if (audioDelay >= 0)
    soundArr = [zeros(numPaddingZeros, size(soundArr, 2)); soundArr];
else
    soundArr = [soundArr; zeros(numPaddingZeros, size(soundArr, 2))];
end

% Write the padded audio signal.
delayedAudioFileName = [audioFileName(1:(end - 4)) '_Delay' num2str(audioDelay) audioFileName((end - 3):end)];
wavwrite(soundArr, samplingRate, bitsPerSample, delayedAudioFileName);

% Combine the padded audio and video into a single movie.
movieFileNameCellArr = cell(size(videoFileNameCellArr));
for k = 1:numel(desiredSizesCellArr);
    % Partition the name of the current video-only file in order to use its
    % component in constructing a name for the current movie.
    [~, videoFileNameNoPathNoExt, videoFileExt] = partitionFileName(videoFileNameCellArr{k});
    % Construct a name for the current movie.
    movieFileNameCellArr{k} = [movieDirPath videoFileNameNoPathNoExt(1:(end - ...
        numel(videoOnlyFileSuffix) + numel(videoFileExt) + 1)) movieFileSuffix];
    % Make a command line call to ffmpeg to combine video and audio into a
    % single movie.
%     cmdline = ['ffmpeg -i "' videoFileNameCellArr{k} '" -i "' delayedAudioFileName ...
%         '" -vcodec copy -acodec pcm_s24le -ab 2304 -ar 96000 -ac 1 -r 24 "' movieFileNameCellArr{k} '"'];
    cmdline = ['ffmpeg -i "' videoFileNameCellArr{k} '" -i "' delayedAudioFileName ...
        '" -vcodec copy -acodec copy -r 24 "' movieFileNameCellArr{k} '"'];
    %helpdlg(cmdline,'system call');

    %eval(['!' cmdline]);
    
    %if(ismac)
    if (0)
        fid = fopen([movieDirPath 'cmdline.txt'], 'w');
        if (fid == -1)
            warning('SoundRestoration:writeCommand:CannotWriteToFile', '');
            return;
        end
        
        fprintf(fid, '%s\n', cmdline);
        fclose(fid);
        
        [status output] = system(['sh -l ' movieDirPath 'cmdline.txt']);
    else
        [status output] = system(cmdline);
    end
    
    %helpdlg(output, 'Output');
    
    % Write the command line input and output to the log file.
    writeToLogFile(['System: audio and video synch call: ' cmdline]);
    writeToLogFile(['System: audio and video synch output: ' output]);
    
    % Update the progress bar.
    if (exist('waitbarStruct', 'var') == 1)
        if getappdata(waitbarStruct(2).handle, 'canceling')
            return;
        end
        if isempty(waitbarStruct(2).total)
            waitbarStruct(2).total = numel(desiredSizesCellArr);
        end
        waitbar(waitbarStruct(2).start + (waitbarStruct(2).length)*(k/numel(desiredSizesCellArr)), ...
            waitbarStruct(2).handle, sprintf('Step %d of %d: synchronizing video %d out of %d', ...
            waitbarStruct(2).index, waitbarStruct(2).totalIndices, ...
            waitbarStruct(2).first + k - 1, waitbarStruct(2).total));
    end
end

return;
% end of the function "imagesAndAudioToMovie"
